#include "jzbus.hpp"
#include "net_zbus_Worker.h"
  
static zbuswrk_t*
	get_worker (JNIEnv* env, jobject obj);
static void 
	put_worker (JNIEnv *env, jobject obj, zbuswrk_t* cli);
static zbuswrk_cfg_t*
	get_config(JNIEnv* env, jobject obj);


JNIEXPORT void JNICALL 
Java_net_zbus_Worker_construct(
	JNIEnv* env, jobject obj, jobject cfg)
{ 
	zbuswrk_t* self = get_worker(env, obj);
	if(self) return;

	zbuswrk_cfg_t* wrk_cfg = get_config(env, cfg);
	
	self = zbuswrk_new(wrk_cfg);
	assert(self);

	put_worker(env, obj, self); 
 
	zbuswrk_cfg_destroy(&wrk_cfg); 
}

JNIEXPORT void JNICALL 
Java_net_zbus_Worker_finalize(
	JNIEnv* env, jobject obj)
{
	zbuswrk_t* self = get_worker(env, obj);
	assert(self);
	zbuswrk_destroy(&self); 
}

JNIEXPORT jobjectArray JNICALL 
Java_net_zbus_Worker_receive(JNIEnv * env, jobject obj, jobjectArray jmsg)
{  
	zmsg_t* reply = NULL;
	if(jmsg){
		reply = zmsg_new();
		jsize req_size = env->GetArrayLength(jmsg);
		for(int i=0; i<req_size; i++){
			jbyteArray frame = (jbyteArray)env->GetObjectArrayElement(jmsg, i);
			jbyte* data = env->GetByteArrayElements(frame, 0);
			jsize size = env->GetArrayLength(frame);
			assert(data); 
			zmsg_push_back(reply, zframe_new(data, size));
			env->ReleaseByteArrayElements(frame, data, JNI_ABORT);
		} 
	}
	zbuswrk_t* self = get_worker(env, obj);
	zmsg_t* res = zbuswrk_recv(self, reply);
	if(!res){
		raise_exception(env, "worker returned null");
		return NULL;
	}

	jsize res_size = zmsg_frame_size(res); 

	jclass cls_byte_array = env->FindClass("[B");
	assert(cls_byte_array);
	jobjectArray result = env->NewObjectArray(res_size, cls_byte_array, 0);
	env->DeleteLocalRef(cls_byte_array);

	for(jsize i=0;i<res_size;i++){
		zframe_t* frame = zmsg_pop_front(res);
		jsize size = zframe_size(frame);
		jbyteArray jframe = env->NewByteArray(size); 
		env->SetByteArrayRegion(jframe, 0, size,(jbyte*) zframe_data(frame));
		env->SetObjectArrayElement(result, i, jframe);
		zframe_destroy(&frame);
	}

	zmsg_destroy(&res);  

	return result; 
}

static zbuswrk_t*
get_worker (JNIEnv* env, jobject obj)
{
	jclass cls = env->GetObjectClass (obj);
	assert (cls);
	jfieldID jhandle = env->GetFieldID (cls, "handle", "J");
	assert (jhandle);
	env->DeleteLocalRef (cls); 

	zbuswrk_t* wrk = (zbuswrk_t*) env->GetLongField(obj, jhandle);
	return wrk;
}
static void 
put_worker(JNIEnv *env, jobject obj, zbuswrk_t* wrk)
{
	jclass cls = env->GetObjectClass (obj);
	assert (cls);
	jfieldID jhandle = env->GetFieldID (cls, "handle", "J");
	assert (jhandle);
	env->DeleteLocalRef (cls); 

	env->SetLongField(obj, jhandle, (jlong)wrk);
}
 
static zbuswrk_cfg_t*
get_config(JNIEnv* env, jobject obj){
	zbuswrk_cfg_t* cfg = zbuswrk_cfg_new();
	jclass cls = env->GetObjectClass (obj);
	assert (cls);

	//service
	jfieldID svc_field = env->GetFieldID(cls, "service", "Ljava/lang/String;");
	assert (svc_field);
	jstring jsvc = (jstring)env->GetObjectField(obj, svc_field);
	const char* svc = env->GetStringUTFChars(jsvc, 0);
	zbuswrk_cfg_set_service(cfg, (char*)svc);
	//printf("service=%s\n",svc);
	env->ReleaseStringUTFChars(jsvc, svc);

	//mode
	jfieldID mode_field = env->GetFieldID(cls, "mode", "Ljava/lang/String;");
	assert (mode_field);
	jstring jmode = (jstring)env->GetObjectField(obj, mode_field);
	const char* mode = env->GetStringUTFChars(jmode, 0);
	zbuswrk_cfg_set_mode(cfg, (char*)mode);
	//printf("mode=%s\n",mode);
	env->ReleaseStringUTFChars(jmode, mode);

	//host & port
	jfieldID host_field = env->GetFieldID(cls, "host", "Ljava/lang/String;");
	assert (host_field);
	jstring jhost = (jstring)env->GetObjectField(obj, host_field);
	const char* host = env->GetStringUTFChars(jhost, 0);
	jfieldID port_field = env->GetFieldID(cls, "port", "I");
	assert (port_field);
	int port = (int)env->GetIntField(obj, port_field); 
	zbuswrk_cfg_set_broker2(cfg, (char*)host, port);
	//printf("host:port=%s:%d\n",host, port);
	env->ReleaseStringUTFChars(jhost, host);

	//registerToken
	jfieldID regtoken_field = env->GetFieldID(cls, "registerToken", "Ljava/lang/String;");
	assert (regtoken_field);
	jstring jregtoken = (jstring)env->GetObjectField(obj, regtoken_field);
	const char* regtoken = env->GetStringUTFChars(jregtoken, 0);
	zbuswrk_cfg_set_regtoken(cfg, (char*)regtoken);
	//printf("regtoken=%s\n",regtoken);
	env->ReleaseStringUTFChars(jregtoken, regtoken);

	//registerToken
	jfieldID acctoken_field = env->GetFieldID(cls, "accessToken", "Ljava/lang/String;");
	assert (acctoken_field);
	jstring jacctoken = (jstring)env->GetObjectField(obj, acctoken_field);
	const char* acctoken = env->GetStringUTFChars(jacctoken, 0);
	zbuswrk_cfg_set_acctoken(cfg, (char*)acctoken);
	//printf("acctoken=%s\n",acctoken);
	env->ReleaseStringUTFChars(jacctoken, acctoken);
	//reconnect
	jfieldID reconnect_field = env->GetFieldID(cls, "reconnect", "I");
	assert (reconnect_field);
	int reconnect = (int)env->GetIntField(obj, reconnect_field); 
	//printf("reconnect=%d\n",reconnect);
	zbuswrk_cfg_set_reconnect(cfg, reconnect); 
	//heartbeat
	jfieldID heartbeat_field = env->GetFieldID(cls, "heartbeat", "I");
	assert (heartbeat_field);
	int heartbeat = (int)env->GetIntField(obj, heartbeat_field); 
	//printf("heartbeat=%d\n",heartbeat);
	zbuswrk_cfg_set_heartbeat(cfg, heartbeat); 
	//timeout
	jfieldID timeout_field = env->GetFieldID(cls, "timeout", "I");
	assert (timeout_field);
	int timeout = (int)env->GetIntField(obj, timeout_field); 
	//printf("timeout=%d\n",timeout);
	zbuswrk_cfg_set_timeout(cfg, timeout); 
	//verbose
	jfieldID verbose_field = env->GetFieldID(cls, "verbose", "Z");
	assert (verbose_field);
	int verbose = (int)env->GetBooleanField(obj, verbose_field);
	zbuswrk_cfg_set_verbose(cfg, verbose);
	

	//printf("%s:%d,%d,%d,%d\n",host, port, timeout, retries, verbose);
	env->DeleteLocalRef (cls);

	return cfg;
}
